package auctionsimulation.market;

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Logger;

import auctionsimulation.communication.command.BidCommand;
import auctionsimulation.communication.command.BuyProductCommand;
import auctionsimulation.communication.command.Command;
import auctionsimulation.communication.command.NewAuctionCommand;
import auctionsimulation.communication.command.OfferNewPriceOnAuctionCommand;
import auctionsimulation.communication.command.SearchAuctionsCommand;
import auctionsimulation.communication.response.AuctionSetResponse;
import auctionsimulation.communication.response.ErrorResponse;
import auctionsimulation.communication.response.NotSuccessfullResponse;
import auctionsimulation.communication.response.OKResponse;
import auctionsimulation.helper.AuctionHelper;
import auctionsimulation.statistics.Statistics;

public class MarketWorker implements Runnable {

	private MarketServer server;
	private Socket sock;
	private ObjectInputStream commandInput;
	private ObjectOutputStream commandOutput;
	private Command cmd;

	public MarketWorker(MarketServer server, Socket sock) {
		this.server = server;
		this.sock = sock;
		try {
			this.commandInput = new ObjectInputStream(sock.getInputStream());
		} catch (IOException e) {
			AuctionHelper.error("was not able to open ObjectInputStream", this,
					e);
		}
		try {
			this.commandOutput = new ObjectOutputStream(sock.getOutputStream());
		} catch (IOException e) {
			AuctionHelper.error("was not able to open ObjectOutputStream",
					this, e);
		}
	}

	@Override
	public void run() {
		try {
			while (!sock.isClosed()) {
				try {
					cmd = (Command) commandInput.readObject();
					Logger.getGlobal().info("Server received command:" + cmd);
					// SEARCHING AUCTIONS========
					if (cmd instanceof SearchAuctionsCommand) {
						SearchAuctionsCommand command = (SearchAuctionsCommand) cmd;
						CopyOnWriteArrayList<? extends Auction> auctions = getAuctions(command
								.getAuctionType());
						if (auctions == null) {
							ErrorResponse er = new ErrorResponse(
									"auction type was invalid, must me reverse, dutch or double");

							try {
								commandOutput.writeObject(er);
							} catch (IOException e) {
								e.printStackTrace();
							}
							continue;
						}
						ArrayList<Auction> result = new ArrayList<Auction>();
						for (Auction a : auctions) {
							if (!a.started || a.isFinished()) {
								continue;
							}
							if (command.getInitiator() != null
									&& command.getInitiator() != a
											.getInitiator()) {
								continue;
							}
							if (!(a instanceof DoubleAuction)) {
								if (!(command.getMinPrice() <= a
										.getCurrentPrice() && a
										.getCurrentPrice() <= command
										.getMaxPrice())) {
									continue;
								}
							} else {
								//TODO: check this if maxprice is right
								if (!(command.getMaxPrice() <= a
										.getCurrentPrice())) {
									continue;
								}
							}
							if (!a.matchTemplate(command.getRequireTemplate())) {
								continue;
							}
							result.add(a);
						}
						AuctionSetResponse asr = new AuctionSetResponse("OK",
								result);
						commandOutput.writeObject(asr);
						// CREATING AUCTIONS========
					} else if (cmd instanceof NewAuctionCommand) {
						if (server.addAuction(((NewAuctionCommand) cmd)
								.getAuction())) {
							commandOutput.writeObject(new OKResponse());
						} else {
							commandOutput.writeObject(new ErrorResponse(
									"server was not able to add auction"));
						}
					}
					// BUYING OF DUTCH OR REVERSE AUCTION PRODUCTS===
					else if (cmd instanceof BuyProductCommand) {
						BuyProductCommand command = (BuyProductCommand) cmd;
						if (!(command.getAuction() instanceof ReverseEnglishAuction || command
								.getAuction() instanceof DutchAuction)) {
							commandOutput
									.writeObject(new ErrorResponse(
											"BuyProductComand can only be sent for a reverse english or dutch auction"));
						} else {
							if (command.getAuction() == null) {
								commandOutput
										.writeObject(new ErrorResponse(
												"Auction in BuyProductCommand must not be null"));
							} else {
								if (server.buyAuction(command.getAuction())) {
									commandOutput.writeObject(new OKResponse());
								} else {
									commandOutput
											.writeObject(new NotSuccessfullResponse());
								}
							}
						}
						// PLACING NEW OFFERS ON AUCTIONS==============
					} else if (cmd instanceof OfferNewPriceOnAuctionCommand) {
						OfferNewPriceOnAuctionCommand command = (OfferNewPriceOnAuctionCommand) cmd;
						if (server.placePriceBid(command.getAuctionType(),
								command.getInitiator(),
								command.getOfferedPrice(),
								command.getOfferedTemplate())) {
							commandOutput.writeObject(new OKResponse());
						} else {
							commandOutput
									.writeObject(new NotSuccessfullResponse());
						}
					} else if (cmd instanceof BidCommand) {
						BidCommand command = (BidCommand) cmd;
						if (server.bidForDoubleAuction(command.getBidder(),
								command.getAuction(), command.getPrice())) {
							commandOutput.writeObject(new OKResponse());
						} else {
							commandOutput
									.writeObject(new NotSuccessfullResponse());
						}
					}
				} catch (ClassCastException e) {
					AuctionHelper.error(
							"command from agent had an invalid classtype",
							this, e);
				} catch (EOFException e) {
				}
			}
		} catch (IOException e) {
			AuctionHelper.error(
					"marketworker was not able to read or write object", this,
					e);
		} catch (ClassNotFoundException e) {
			AuctionHelper
					.error("classtype of object that the maretworker read was not found",
							this, e);
		}
	}

	public CopyOnWriteArrayList<? extends Auction> getAuctions(String selector) {
		switch (selector) {
		case "reverse":
			return server.getReverseEnglishAuctions();
		case "dutch":
			return server.getDutchAuctions();
		case "double":
			return server.getDoubleAuctions();
		}
		return null;
	}

}
